/*
 * Copyright 2003-2005 (c) Live Media Pty Ltd. <argot@einet.com.au> 
 *
 * This software is licensed under the Argot Public License 
 * which may be found in the file LICENSE distributed 
 * with this software.
 *
 * More information about this license can be found at
 * http://www.einet.com.au/License
 * 
 * The Developer of this software is Live Media Pty Ltd,
 * PO Box 4591, Melbourne 3001, Australia.  The license is subject 
 * to the law of Victoria, Australia, and subject to exclusive 
 * jurisdiction of the Victorian courts.
 */

#include "../Types.h"
#include "../TypeMap.h"
#include "../ReferenceTypeMap.h"
#include "../TypeHelper.h"

#include "../util/intmap.h"

#include "TypeTriple.h"
#include "TypeLink.h"
#include "TypeClient.h"
#include "DynamicClientTypeMap.h"

static struct type_map_vtable dynamicClientMapVtable =
{
	&_type_map_map,
	&_dynamic_client_map_get_id,
	&_type_map_get_name,
	&_type_map_get_reader,
	&_type_map_get_writer,
	&_type_map_get_structure,
	&_dynamic_client_map_get_library_id,
	&_dynamic_client_map_get_mapped_id,
	&_type_map_is_valid_id,
	&_type_map_is_valid_name,
	&_type_map_get_size
};

void dynamic_client_type_map_init( dynamic_client_type_map * map, type_library * library, type_client * client )
{
	reference_type_map_init( &map->ref_map, library, NULL );
	reference_type_map_set_reference( &map->ref_map, (type_map*) &map->ref_map );
	map->ref_map.map.__vt = &dynamicClientMapVtable;
	map->client = client;
	intmap_init( &map->stack, 3 );
}

static int resolve_type( dynamic_client_type_map * map, int systemId )
{
	// See if the type has already been mapped.
	int x;
	
	x = _type_map_get_mapped_id( (type_map*) map, systemId );
	if ( x > NOTYPE )
		return x;
		
	// No mapping..  Map this type.
	// Requires the Name and structure.
	// The server has the definative map.  So we send
	// the details over and expect a response of the id
	// to use.
	type_library * library = type_map_get_library( (type_map*) map );
	type_name * name = type_library_get_name( library, systemId );
	
	
	if ( INTMAP_OK == intmap_find( &map->stack, (int) name ))
	{
		// If we have a self referenced type we first need to get
		// and ID for the Dynamic type map.  From that we can then
		// check the full definition because we can describe it.

		x = type_client_reserve_type( map->client, name );
		if ( x <= NOTYPE )
			return x;
	}
	else
	{
		// We haven't resolved the type yet so put it on the stack
		// and resolve it.
		intmap_add( &map->stack, (int)name, (int)name );
	
		// As we write out the definition of this type we may
		// come accross other types that must be resolved.  This
		// will recurively resolve each type.
		type_element * structure = type_library_get_structure( library, systemId );
		if ( structure == NULL )
			return type_library_get_error( library );

		type_buffer * definition = type_helper_resolve_structure( (reference_type_map*)map, structure );

		x = type_client_resolve_type( map->client, name, definition );
		if ( x <= NOTYPE )
			return x;
			
		intmap_find( &map->stack, (int)name );
		intmap_remove( &map->stack );
	}
	
	if ( x == NOTYPE )
	{
		/* TODO fix magic number */
		return -1;
	}

	type_map_map( (type_map*)map, x, systemId );

	return x;
}
	
static int resolve_reverse( dynamic_client_type_map * map, int id )
{
	type_triple typeInfo;

	int rc = type_client_resolve_reverse( map->client, &typeInfo, id);
	if ( rc != TYPE_OK )
		return rc;
	
	type_library * library = type_map_get_library( (type_map*) map );
	
	int systemid = type_library_get_id( library, typeInfo.name );

	type_buffer * definition = type_helper_resolve_structure( (reference_type_map*) map, type_library_get_structure( library, systemid ) );

	if ( 0 == memcmp( definition, typeInfo.definition, definition->size ) )
	{
		// The definition matches..  so it is ok.  Map it.
		type_map_map( (type_map*) map, id, systemid );
		return TYPE_OK;
	}
	
	return -1;
}
	

int _dynamic_client_map_get_mapped_id( type_map * map, int systemId) 
{
	// call super get id.
	int rc = _type_map_get_mapped_id( (type_map *) map, systemId );
	if ( rc > NOTYPE )
		return rc;
		
	return resolve_type( (dynamic_client_type_map*)map, systemId );
}

int _dynamic_client_map_get_id( type_map * map, type_name * name) 
{
	int rc = _type_map_get_id( (type_map*) map, name );
	if ( rc > NOTYPE )
		return rc;

	type_library * library = type_map_get_library( (type_map*) map );
	
	return resolve_type( (dynamic_client_type_map*) map, type_library_get_id( library, name ) );
}

int _dynamic_client_map_get_library_id( type_map * map, int id) 
{
	int rc = _type_map_get_library_id( map, id );
	if ( rc > NOTYPE )
		return rc;

	rc = resolve_reverse( (dynamic_client_type_map*) map, id );
	if ( rc <= NOTYPE ) return rc;

	return _type_map_get_library_id( (type_map*)map, id );
}


